#include <arduino.h>
#include <EEPROM.h>
#include <Arduino_JSON.h>

#define DEVICE_ADDRESS 0 // 设备站号

int AiAddressArray[8]; // A0:14,A1:15,A2:16,A3:17,A4:18,A5:19,A6:20,A7:21 每个模拟引脚2个寄存器,保存16位无符号数值
unsigned long counter[4];
unsigned long recTime;
String recData;

// arduino 软重启
void(* resetFunc) (void) = 0;

void setup() {
  AiAddressArray[0] = 14;
  AiAddressArray[1] = 18;
  AiAddressArray[2] = 22;
  AiAddressArray[3] = 26;
  AiAddressArray[4] = 30;
  AiAddressArray[5] = 34;
  AiAddressArray[6] = 38;
  AiAddressArray[7] = 42;

  recTime = millis();
  Serial.begin(115200);
}

void loop() {
  if (Serial.available() > 0){
    char buff = Serial.read();
    //Serial.println(buff);
    recData = recData + String(buff);
    //Serial.println(recData);
    
    if (recData != "")
    {
      JSONVar myObject = JSON.parse(recData);
      if (JSON.typeof(myObject) != "undefined") {
        executionTask(myObject);recData = "";
      }
    }
    recTime = millis();
  }

  if (millis() - recTime >= 15 && recData != ""){recData = "";recTime = millis();} //超时清除缓冲区
}

void onChange1_D2()
{
    counter[0]++;
}
void onChange1_D3()
{
    counter[1]++;
}
void onChange1_D4()
{
    counter[2]++;
}
void onChange1_D5()
{
    counter[3]++;
}

word byteToWord(byte a,byte b)
{
  // word range 0 to 65535
  word temp = (a << 8) | b;
  return temp;
}

byte * WordToByte(word a)
{
  static byte value[2];
  value[0] = a >> 8;
  value[1] = a & 255;
  return value;
}

void executionTask(JSONVar myObject){
  JSONVar sendObject;
  if (myObject.hasOwnProperty("type")) {
    String _type = String((const char*) myObject["type"]);
    //Serial.println(_type);
    if (_type == "IO-SET")
    {
      if (myObject.hasOwnProperty("channel") == false) {return;}
      if (myObject.hasOwnProperty("mode") == false) {return;}
      String mode = String((const char*) myObject["mode"]);
      int channel = (int)myObject["channel"];
      pinMode(channel,mode == "OUTPUT" ? OUTPUT:mode == "INPUT" ? INPUT:INPUT_PULLUP); // || mode == "COUNTER" 
      sendObject["type"] = _type;
      sendObject["channel"] = channel;
      sendObject["mode"] = mode;
      if(mode == "COUNTER")
      {
        switch(channel)
        {
          //LOW 当引脚为低电平时，触发中断
          //CHANGE 当引脚电平发生改变时，触发中断
          //RISING 当引脚由低电平变为高电平时，触发中断
          //FALLING 当引脚由高电平变为低电平时，触发中断.
          case 2: attachInterrupt(digitalPinToInterrupt(2), onChange1_D2, FALLING); delay(50); counter[0] = 0; break;
          case 3: attachInterrupt(digitalPinToInterrupt(3), onChange1_D3, FALLING); delay(50); counter[1] = 0; break;
          case 4: attachInterrupt(digitalPinToInterrupt(4), onChange1_D4, FALLING); delay(50); counter[2] = 0; break;
          case 5: attachInterrupt(digitalPinToInterrupt(5), onChange1_D5, FALLING); delay(50); counter[3] = 0; break;
        }
      }
    }

    if (_type == "IO-WRITE")
    {
      if (myObject.hasOwnProperty("channel") == false) {return;}
      if (myObject.hasOwnProperty("signal") == false) {return;}
      digitalWrite((int)myObject["channel"],String((const char*)myObject["signal"]) == "HIGH" ? HIGH:LOW);
      sendObject["type"] = _type;
      sendObject["channel"] = (int)myObject["channel"];
      sendObject["signal"] = String((const char*)myObject["signal"]) == "HIGH" ? "HIGH":"LOW";
    }

    if (_type == "IO-READ")
    {
      if (myObject.hasOwnProperty("channel") == false) {return;}
      int channel =(int)myObject["channel"];
      sendObject["type"] = _type;
      sendObject["channel"] = channel;
      sendObject["signal"] = digitalRead(channel) == HIGH ? "HIGH":"LOW";
    }

    if (_type == "COUNTER-READ")
    {
      if (myObject.hasOwnProperty("channel") == false) {return;}
      int channel = (int)myObject["channel"];
      sendObject["type"] = _type;
      sendObject["channel"] = channel;
      unsigned long readValue = 0;
      switch(channel)
      {
        case 2: readValue = counter[0]; counter[0] = 0; break;
        case 3: readValue = counter[1]; counter[1] = 0; break;
        case 4: readValue = counter[2]; counter[2] = 0; break;
        case 5: readValue = counter[3]; counter[3] = 0; break;
      }
        sendObject["count"] = readValue;
    }

    if (_type == "COUNTER-CLOSE")
    {
      if (myObject.hasOwnProperty("channel") == false) {return;}
      int channel = (int)myObject["channel"];
      sendObject["type"] = _type;
      sendObject["channel"] = channel;
      switch(channel)
      {
        case 2: detachInterrupt(digitalPinToInterrupt(2)); counter[0] = 0; break;
        case 3: detachInterrupt(digitalPinToInterrupt(3)); counter[1] = 0; break;
        case 4: detachInterrupt(digitalPinToInterrupt(4)); counter[2] = 0; break;
        case 5: detachInterrupt(digitalPinToInterrupt(5)); counter[3] = 0; break;
        default : sendObject["channel"] = -1; break;
      }
    }

    if (_type == "REBOOT")
    {
      sendObject["type"] = _type;
      sendObject["status"] = "OK";
    }

    if (_type == "AI-WRITE")
    {
      if (myObject.hasOwnProperty("channel") == false) {return;}
      if (myObject.hasOwnProperty("value") == false) {return;}
      int vlaue = (int)myObject["value"];
      int channel = (int)myObject["channel"];
      analogWrite (channel,vlaue); //值：0（始终导通）到255（始终关断）之间。
      sendObject["type"] = _type;
      sendObject["channel"] = channel;
      sendObject["value"] = vlaue;
    }

    if (_type == "AI-READ")
    {
      if (myObject.hasOwnProperty("channel") == false) {return;}
      sendObject["type"] = _type;
      int channel = (int)myObject["channel"];
      sendObject["channel"] = channel;

       if (channel >= 14 && channel <= 21)
      {
        int valueRead = analogRead(channel);
        sendObject["value"] = valueRead; // analogRead values go from 0 to 1023

        // 通过校准值计算电压
        int startAddress = AiAddressArray[channel - 14];
        byte HiByte = EEPROM.read(startAddress);
        byte LoByte = EEPROM.read(startAddress + 1);
        word adc3300 = byteToWord(HiByte,LoByte);
        double adcoffset = ((adc3300 / 10.0) * 100.0) / 330.0;
        double voltage = ((valueRead * 100.0) / adcoffset) * 10.0;
        sendObject["voltage"] = round(voltage);
      }else{
        sendObject["value"] = -1;
      }
    }

    if (_type == "AI-SETREF")
    {
      // https://blog.csdn.net/weixin_43031092/article/details/108202623
      // https://www.arduino.cc/reference/en/language/functions/analog-io/analogreference/
      // Configures the reference voltage used for analog input (i.e. the value used as the top of the input range). The options are:
      // Arduino AVR Boards (Uno, Mega, Leonardo, etc.)
      // DEFAULT: the default analog reference of 5 volts (on 5V Arduino boards) or 3.3 volts (on 3.3V Arduino boards)
      // INTERNAL: a built-in reference, equal to 1.1 volts on the ATmega168 or ATmega328P and 2.56 volts on the ATmega32U4 and ATmega8 (not available on the Arduino Mega)
      // INTERNAL1V1: a built-in 1.1V reference (Arduino Mega only)
      // INTERNAL2V56: a built-in 2.56V reference (Arduino Mega only)
      // EXTERNAL: the voltage applied to the AREF pin (0 to 5V only) is used as the reference.
      if (myObject.hasOwnProperty("aref") == false) {return;}
      sendObject["type"] = _type;
      int aref = (int)myObject["aref"];
      sendObject["aref"] = aref;
      switch (aref)
      {
        case 0:
          analogReference(DEFAULT);
          break;
        case 1:
          analogReference(EXTERNAL);
          break;
        case 2:
          analogReference(INTERNAL);
          break;
        default:
          sendObject["aref"] = -1;
          break; 
      }
    }

    if (_type == "AI-CALIBRATION") // 校准引脚电压 3.3V
    {
      if (myObject.hasOwnProperty("channel") == false) {return;}
      if (myObject.hasOwnProperty("operat") == false) {return;}
      sendObject["type"] = _type;
      int channel = (int)myObject["channel"];
      sendObject["channel"] = channel;

      if (channel >= 14 && channel <= 21)
      {
        if ((int)myObject["operat"] == 1){ // 1=写校准 2=读校准
          byte *value;
          double count = 30.0;
          int totleVol = 0;
          word aveVol = 0;
          
          pinMode(channel,INPUT);
          for (int i = 0; i < count; i++){
            int CurrRead = analogRead(channel);
            totleVol += CurrRead;
            Serial.print("Read value(");
            Serial.print(i + 1);
            Serial.print("): ");
            Serial.println(CurrRead);
            delay(100);
          }

          aveVol = (word)((totleVol / count) * 10);
          Serial.print("Totle value: ");
          Serial.println(totleVol);
          Serial.print("Calibration value: ");
          Serial.println(aveVol);

          value = WordToByte(aveVol);
          byte HiByte = *(value + 0);
          byte LoByte = *(value + 1);

          int startAddress = AiAddressArray[channel - 14];
          EEPROM.write(startAddress,HiByte);
          EEPROM.write(startAddress + 1,LoByte);
          HiByte = EEPROM.read(startAddress);
          LoByte = EEPROM.read(startAddress + 1);
          sendObject["value"] = (int)byteToWord(HiByte,LoByte);
        }else{
          int startAddress = AiAddressArray[channel - 14];
          byte HiByte = EEPROM.read(startAddress);
          byte LoByte = EEPROM.read(startAddress + 1);
          word adc3300 = byteToWord(HiByte,LoByte);
          double adcoffset = ((adc3300 / 10.0) * 100.0) / 330.0;
          sendObject["value"] = (int)adc3300;
          sendObject["offset"] = round(adcoffset);
        }
      }else
      {
        sendObject["value"] = -1;
        sendObject["channel"] = -1;
      }
    }

    if (_type == "PIN-READ")
    {
      Serial.print("{\"type\":\"");
      Serial.print(_type);
      Serial.print("\",\"counter\":\"");
      Serial.print("D2:2,D3:3,D4:4,D5:5");
      Serial.print("\",\"pwm\":\"");
      Serial.print("D3:3,D5:5,D6:6,D9:9,D10:10,D11:11");
      Serial.print("\",\"analog\":\"");
      Serial.print("A0:14,A1:15,A2:16,A3:17,A4:18,A5:19,A6:20,A7:21");
      Serial.print("\",\"digital\":\"");
      Serial.println("D2:2,D3:3,D4:4,D5:5,D6:6,D7:7,D8:8,D9:9,D10:10,D11:11,D12:12,D13:13,A0:14,A1:15,A2:16,A3:17,A4:18,A5:19\"}");
    }

    if (_type == "CARD-ADDRESS")
    {
      if (myObject.hasOwnProperty("operat") == false) {return;}
      sendObject["type"] = _type;
      int operat = (int)myObject["operat"];
      if (operat == 1)
      {
        if (myObject.hasOwnProperty("address") == false) {return;}
        int address = (int)myObject["address"];
        sendObject["address"] = address;
        if (address < 0 || address > 255)
        {
          sendObject["address"] = -1;
        }else{
          EEPROM.write(DEVICE_ADDRESS,(byte)address);
        }
      }else{
        sendObject["address"] = (int)EEPROM.read(DEVICE_ADDRESS);
      }
    }

    if (_type == "CARD-INFO")
    {
      sendObject["type"] = _type;
      sendObject["name"] = "ExpandingBoard - arduino nano";
      sendObject["address"] = (int)EEPROM.read(DEVICE_ADDRESS);
      sendObject["version"] = "1.4";
      sendObject["website"] = "www.yfyun.xin / lovecxu.taobao.com";
    }

    String jsonMessage = JSON.stringify(sendObject);
    if (jsonMessage != ""){Serial.println(jsonMessage);}
    if (_type == "REBOOT"){delay(200);resetFunc();}
  }
}
